package vm

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"strings"
)

type VirtualMachineServices struct {
	//dataVolumeTemplate string
}

// ListPods kubectl get pods
func (vms *VirtualMachineServices) ListPods(c *gin.Context) {
	namespace := c.Query("namespace")
	var kubectlExec *exec.Cmd
	if namespace == "" {
		kubectlExec = exec.Command("kubectl", "get", "pods", "--all-namespaces", "-o", "json")
	} else {
		kubectlExec = exec.Command("kubectl", "get", "pods", "-n", namespace, "-o", "json")
	}
	var stdout, stderr bytes.Buffer
	kubectlExec.Stdout = &stdout
	kubectlExec.Stderr = &stderr
	err := kubectlExec.Run()
	_, errStr := string(stdout.Bytes()), string(stderr.Bytes())
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"status": "error", "message": err.Error() + ": " + errStr})
		return
	}
	resultByte := stdout.Bytes()
	var v interface{}
	err = json.Unmarshal(resultByte, &v)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"status": "error", "message": err.Error()})
		return
	}
	resultResult := v.(map[string]interface{})

	c.JSON(http.StatusOK, gin.H{"status": "success", "message": resultResult})
}

// ListDataVolumes kubectl get dv
func (vms *VirtualMachineServices) ListDataVolumes(c *gin.Context) {
	namespace := c.Query("namespace")
	var kubectlExec *exec.Cmd
	if namespace == "" {
		kubectlExec = exec.Command("kubectl", "get", "dv", "--all-namespaces", "-o", "json")
	} else {
		kubectlExec = exec.Command("kubectl", "get", "dv", "-n", namespace, "-o", "json")
	}
	var stdout, stderr bytes.Buffer
	kubectlExec.Stdout = &stdout
	kubectlExec.Stderr = &stderr
	err := kubectlExec.Run()
	_, errStr := string(stdout.Bytes()), string(stderr.Bytes())
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"status": "error", "message": err.Error() + ": " + errStr})
		return
	}
	resultByte := stdout.Bytes()
	var v interface{}
	err = json.Unmarshal(resultByte, &v)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"status": "error", "message": err.Error()})
		return
	}
	resultResult := v.(map[string]interface{})
	itemsList := resultResult["items"].([]interface{})
	dvSlice := make([]DataVolume, 0)
	for _, item := range itemsList {
		itemMap := item.(map[string]interface{})
		dv := DataVolume{
			Name:             itemMap["metadata"].(map[string]interface{})["name"].(string),
			Namespace:        itemMap["metadata"].(map[string]interface{})["namespace"].(string),
			Phase:            "NoStatus",
			Progress:         "-",
			Url:              itemMap["spec"].(map[string]interface{})["source"].(map[string]interface{})["http"].(map[string]interface{})["url"].(string),
			Storage:          itemMap["spec"].(map[string]interface{})["pvc"].(map[string]interface{})["resources"].(map[string]interface{})["requests"].(map[string]interface{})["storage"].(string),
			StorageClassName: itemMap["spec"].(map[string]interface{})["pvc"].(map[string]interface{})["storageClassName"].(string),
			VolumeMode:       itemMap["spec"].(map[string]interface{})["pvc"].(map[string]interface{})["volumeMode"].(string),
		}
		if _, ok := itemMap["status"]; ok {
			dv.Phase = itemMap["status"].(map[string]interface{})["phase"].(string)
			dv.Progress = itemMap["status"].(map[string]interface{})["progress"].(string)
		}
		dvSlice = append(dvSlice, dv)
	}
	dvResult := DataVolumeResponse{
		Kind:  "DataVolumes",
		Items: dvSlice,
	}
	c.JSON(http.StatusOK, gin.H{"status": "success", "message": dvResult})
}

// ListPVCEvents kubectl get events
func (vms *VirtualMachineServices) ListPVCEvents(c *gin.Context) {
	namespace := c.DefaultQuery("namespace", "default")
	pvcName := c.Query("name")
	var kubectlExec *exec.Cmd
	if pvcName == "" {
		kubectlExec = exec.Command("kubectl", "get", "events", "--field-selector", "involvedObject.kind=PersistentVolumeClaim", "-o", "json", "-n", namespace)
	} else {
		nameSelector := "involvedObject.name=" + pvcName
		//  "--field-selector", "involvedObject.kind=PersistentVolumeClaim"
		kubectlExec = exec.Command("kubectl", "get", "events", "--field-selector", nameSelector, "-o", "json", "-n", namespace)
	}
	var stdout, stderr bytes.Buffer
	kubectlExec.Stdout = &stdout
	kubectlExec.Stderr = &stderr
	err := kubectlExec.Run()
	_, errStr := string(stdout.Bytes()), string(stderr.Bytes())
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"status": "error", "message": err.Error() + ": " + errStr})
		return
	}
	resultByte := stdout.Bytes()
	var v interface{}
	err = json.Unmarshal(resultByte, &v)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"status": "error", "message": err.Error()})
		return
	}
	resultResult := v.(map[string]interface{})
	itemsList := resultResult["items"].([]interface{})
	pvcSlice := make([]PVCEvents, 0)
	for _, item := range itemsList {
		itemMap := item.(map[string]interface{})
		if itemMap["involvedObject"].(map[string]interface{})["kind"].(string) != "PersistentVolumeClaim" {
			return
		}
		dv := PVCEvents{
			Name:           itemMap["involvedObject"].(map[string]interface{})["name"].(string),
			Namespace:      itemMap["involvedObject"].(map[string]interface{})["namespace"].(string),
			Kind:           itemMap["involvedObject"].(map[string]interface{})["kind"].(string),
			FirstTimestamp: itemMap["firstTimestamp"].(string),
			LastTimestamp:  itemMap["lastTimestamp"].(string),
			Reason:         itemMap["reason"].(string),
			Type:           itemMap["type"].(string),
			Count:          itemMap["count"].(float64),
			Message:        itemMap["message"].(string),
		}

		pvcSlice = append(pvcSlice, dv)
	}
	pvcResult := PVCEventsResponse{
		Kind:  "Events",
		Items: pvcSlice,
	}
	c.JSON(http.StatusOK, gin.H{"status": "success", "message": pvcResult})
}

// ListResizingPVC kubectl get events
func (vms *VirtualMachineServices) ListResizingPVC(c *gin.Context) {
	//namespace := c.DefaultQuery("namespace", "default")
	var kubectlExec *exec.Cmd
	kubectlExec = exec.Command("kubectl", "get", "events", "--field-selector", "involvedObject.kind=PersistentVolumeClaim", "-o", "json", "-A")
	var stdout, stderr bytes.Buffer
	kubectlExec.Stdout = &stdout
	kubectlExec.Stderr = &stderr
	err := kubectlExec.Run()
	_, errStr := string(stdout.Bytes()), string(stderr.Bytes())
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"status": "error", "message": err.Error() + ": " + errStr})
		return
	}
	resultByte := stdout.Bytes()
	var v interface{}
	err = json.Unmarshal(resultByte, &v)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"status": "error", "message": err.Error()})
		return
	}
	resultResult := v.(map[string]interface{})
	itemsList := resultResult["items"].([]interface{})
	resizedPVCSlice := make([]PVCEvents, 0)
	resizingPVCSlice := make([]PVCEvents, 0)
	successPVCSlice := make([]PVCEvents, 0)
	for _, item := range itemsList {
		itemMap := item.(map[string]interface{})
		if itemMap["involvedObject"].(map[string]interface{})["kind"].(string) != "PersistentVolumeClaim" {
			return
		}
		if itemMap["reason"] == "Resizing" {
			dv := PVCEvents{
				Name:           itemMap["involvedObject"].(map[string]interface{})["name"].(string),
				Namespace:      itemMap["involvedObject"].(map[string]interface{})["namespace"].(string),
				Kind:           itemMap["involvedObject"].(map[string]interface{})["kind"].(string),
				FirstTimestamp: itemMap["firstTimestamp"].(string),
				LastTimestamp:  itemMap["lastTimestamp"].(string),
				Reason:         itemMap["reason"].(string),
				Type:           itemMap["type"].(string),
				Count:          itemMap["count"].(float64),
				Message:        itemMap["message"].(string),
			}
			resizedPVCSlice = append(resizedPVCSlice, dv)
		}
		if itemMap["reason"] == "VolumeResizeSuccessful" {
			dv := PVCEvents{
				Name:           itemMap["involvedObject"].(map[string]interface{})["name"].(string),
				Namespace:      itemMap["involvedObject"].(map[string]interface{})["namespace"].(string),
				Kind:           itemMap["involvedObject"].(map[string]interface{})["kind"].(string),
				FirstTimestamp: itemMap["firstTimestamp"].(string),
				LastTimestamp:  itemMap["lastTimestamp"].(string),
				Reason:         itemMap["reason"].(string),
				Type:           itemMap["type"].(string),
				Count:          itemMap["count"].(float64),
				Message:        itemMap["message"].(string),
			}
			successPVCSlice = append(successPVCSlice, dv)
		}
	}
	count := 0
	for _, val1 := range resizedPVCSlice {
		count = 0
		for _, val2 := range successPVCSlice {
			if val1.Name == val2.Name && val1.Namespace == val2.Namespace {
				break
			} else {
				count += 1
			}
		}
		if count == len(successPVCSlice) {
			resizingPVCSlice = append(resizingPVCSlice, val1)
		}
	}
	pvcResult := ResizingPVCResponse{
		Kind:          "Resize",
		ResizedItems:  resizedPVCSlice,
		ResizingItems: resizingPVCSlice,
		SuccessItems:  successPVCSlice,
	}
	c.JSON(http.StatusOK, gin.H{"status": "success", "message": pvcResult})
}

// CreateDataVolume kubectl get events
func (vms *VirtualMachineServices) CreateDataVolume(c *gin.Context) {
	namespace := c.DefaultPostForm("namespace", "default")
	dvName := c.PostForm("name")
	dvUrl := c.PostForm("url")
	dvStorage := c.PostForm("storage")
	dvSCName := c.DefaultPostForm("scname", "curve")
	if dvName == "" || dvUrl == "" || dvStorage == "" {
		c.JSON(http.StatusOK, gin.H{"status": "error", "message": "missing argument"})
		return
	}

	dvTemplate := getDataVolumeTemplate(dvName, namespace, dvUrl, dvStorage, dvSCName)
	yamlByte, err := yaml.Marshal(dvTemplate)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"status": "error", "message": err.Error()})
		return
	}
	yamlFileName := "/tmp/datavolume-" + uuid.New().String() + ".yaml"
	_, err = os.OpenFile(yamlFileName, os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"status": "error", "message": err.Error()})
		return
	}
	err = ioutil.WriteFile(yamlFileName, yamlByte, 0644)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"status": "error", "message": err.Error()})
		return
	}

	kubectlExec := exec.Command("kubectl", "apply", "-f", yamlFileName, "-o", "json")
	var stdout, stderr bytes.Buffer
	kubectlExec.Stdout = &stdout
	kubectlExec.Stderr = &stderr
	err = kubectlExec.Run()
	_, errStr := string(stdout.Bytes()), string(stderr.Bytes())
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"status": "error", "message": err.Error() + ": " + errStr})
		return
	}
	resultByte := stdout.Bytes()
	var v interface{}
	err = json.Unmarshal(resultByte, &v)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"status": "error", "message": err.Error()})
		return
	}
	resultResult := v.(map[string]interface{})

	c.JSON(http.StatusOK, gin.H{"status": "success", "message": resultResult})
}

// DeleteDataVolume kubectl get events
func (vms *VirtualMachineServices) DeleteDataVolume(c *gin.Context) {
	namespace := c.DefaultPostForm("namespace", "default")
	dvName := c.PostForm("name")
	if dvName == "" {
		c.JSON(http.StatusOK, gin.H{"status": "error", "message": "missing argument"})
		return
	}
	kubectlExec := exec.Command("kubectl", "delete", "dv", dvName, "-n", namespace)
	var stdout, stderr bytes.Buffer
	kubectlExec.Stdout = &stdout
	kubectlExec.Stderr = &stderr
	err := kubectlExec.Run()
	stdStr, errStr := string(stdout.Bytes()), string(stderr.Bytes())
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"status": "error", "message": err.Error() + ": " + errStr})
		return
	}
	c.JSON(http.StatusOK, gin.H{"status": "success", "message": stdStr})
}

// ExpandDataVolume kubectl get pvc
func (vms *VirtualMachineServices) ExpandDataVolume(c *gin.Context) {
	namespace := c.DefaultPostForm("namespace", "default")
	diskName := c.PostForm("name")
	diskStorage := c.PostForm("storage")
	kubectlExec := exec.Command("kubectl", "get", "pvc", diskName, "-o", "json", "-n", namespace)
	var stdout, stderr bytes.Buffer
	kubectlExec.Stdout = &stdout
	kubectlExec.Stderr = &stderr
	err := kubectlExec.Run()
	_, errStr := string(stdout.Bytes()), string(stderr.Bytes())
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"status": "error", "message": err.Error() + ": " + errStr})
		return
	}
	resultByte := stdout.Bytes()
	var v interface{}
	err = json.Unmarshal(resultByte, &v)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"status": "error", "message": err.Error()})
		return
	}
	resultResult := v.(map[string]interface{})
	resultResult["spec"].(map[string]interface{})["resources"].(map[string]interface{})["requests"].(map[string]interface{})["storage"] = diskStorage

	yamlByte, err := yaml.Marshal(resultResult)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"status": "error", "message": err.Error()})
		return
	}

	yamlFileName := "/tmp/expand-disk-" + uuid.New().String() + ".yaml"
	_, err = os.OpenFile(yamlFileName, os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"status": "error", "message": err.Error()})
		return
	}
	err = ioutil.WriteFile(yamlFileName, yamlByte, 0644)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"status": "error", "message": err.Error()})
		return
	}

	kubectlApplyExec := exec.Command("kubectl", "apply", "-f", yamlFileName, "-o", "json")

	var stdoutApply, stderrApply bytes.Buffer
	kubectlApplyExec.Stdout = &stdoutApply
	kubectlApplyExec.Stderr = &stderrApply
	err = kubectlApplyExec.Run()
	_, errStrApply := string(stdoutApply.Bytes()), string(stderrApply.Bytes())
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"status": "error", "message": err.Error() + ": " + errStrApply})
		return
	}
	applyResultByte := stdoutApply.Bytes()
	var v1 interface{}
	err = json.Unmarshal(applyResultByte, &v1)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"status": "error", "message": err.Error()})
		return
	}
	applyResultResult := v.(map[string]interface{})

	c.JSON(http.StatusOK, gin.H{"status": "success", "message": applyResultResult})
}

//// CreateNetworkInterface kubectl get events
//func (vms *VirtualMachineServices) CreateNetworkInterface(c *gin.Context) {
//	namespace := c.DefaultPostForm("namespace", "default")
//	dvName := c.PostForm("name")
//	dvUrl := c.PostForm("url")
//	dvStorage := c.PostForm("storage")
//	dvSCName := c.DefaultPostForm("scname", "curve")
//	if dvName == "" || dvUrl == "" || dvStorage == "" {
//		c.JSON(http.StatusOK, gin.H{"status": "error", "message": "missing argument"})
//		return
//	}
//
//	dvTemplate := getDataVolumeTemplate(dvName, namespace, dvUrl, dvStorage, dvSCName)
//	yamlByte, err := yaml.Marshal(dvTemplate)
//	if err != nil {
//		c.JSON(http.StatusOK, gin.H{"status": "error", "message": err.Error()})
//		return
//	}
//	yamlFileName := "/tmp/datavolume-" + uuid.New().String() + ".yaml"
//	_, err = os.OpenFile(yamlFileName, os.O_CREATE|os.O_WRONLY, 0644)
//	if err != nil {
//		c.JSON(http.StatusOK, gin.H{"status": "error", "message": err.Error()})
//		return
//	}
//	err = ioutil.WriteFile(yamlFileName, yamlByte, 0644)
//	if err != nil {
//		c.JSON(http.StatusOK, gin.H{"status": "error", "message": err.Error()})
//		return
//	}
//
//	kubectlExec := exec.Command("kubectl", "apply", "-f", yamlFileName, "-o", "json")
//	var stdout, stderr bytes.Buffer
//	kubectlExec.Stdout = &stdout
//	kubectlExec.Stderr = &stderr
//	err = kubectlExec.Run()
//	_, errStr := string(stdout.Bytes()), string(stderr.Bytes())
//	if err != nil {
//		c.JSON(http.StatusOK, gin.H{"status": "error", "message": err.Error() + ": " + errStr})
//		return
//	}
//	resultByte := stdout.Bytes()
//	var v interface{}
//	err = json.Unmarshal(resultByte, &v)
//	if err != nil {
//		c.JSON(http.StatusOK, gin.H{"status": "error", "message": err.Error()})
//		return
//	}
//	resultResult := v.(map[string]interface{})
//
//	c.JSON(http.StatusOK, gin.H{"status": "success", "message": resultResult})
//}

// ListHosts kubectl get dv
func (vms *VirtualMachineServices) ListHosts(c *gin.Context) {
	kubectlExec := exec.Command("kubectl", "get", "nodes", "-o", "json")
	var stdout, stderr bytes.Buffer
	kubectlExec.Stdout = &stdout
	kubectlExec.Stderr = &stderr
	err := kubectlExec.Run()
	_, errStr := string(stdout.Bytes()), string(stderr.Bytes())
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"status": "error", "message": err.Error() + ": " + errStr})
		return
	}
	resultByte := stdout.Bytes()
	var v interface{}
	err = json.Unmarshal(resultByte, &v)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"status": "error", "message": err.Error()})
		return
	}
	resultResult := v.(map[string]interface{})
	itemsList := resultResult["items"].([]interface{})
	hostSlice := make([]HostInfo, 0)
	for _, item := range itemsList {
		infoList := item.(map[string]interface{})["status"].(map[string]interface{})["addresses"].([]interface{})
		hostInfo := HostInfo{
			HostName:  "",
			IpAddress: "",
		}
		for _, infoItem := range infoList {
			if infoItem.(map[string]interface{})["type"] == "InternalIP" {
				hostInfo.IpAddress = infoItem.(map[string]interface{})["address"].(string)
			}
			if infoItem.(map[string]interface{})["type"] == "Hostname" {
				hostInfo.HostName = infoItem.(map[string]interface{})["address"].(string)
			}
		}
		hostSlice = append(hostSlice, hostInfo)
	}
	hostInfoResult := HostInfoResponse{
		Kind:  "HostInfo",
		Items: hostSlice,
	}
	c.JSON(http.StatusOK, gin.H{"status": "success", "message": hostInfoResult})
}

func (vms *VirtualMachineServices) HarvesterApi(c *gin.Context) {
	apiRequest := HarvesterApiRequest{}
	c.ShouldBind(&apiRequest)
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr}
	if apiRequest.MethodType == "GET" {
		requestUrl := harvesterUrl + apiRequest.APIName
		res, err := client.Get(requestUrl)
		defer res.Body.Close()
		if err != nil {
			c.JSON(http.StatusOK, err.Error())
		}
		body, err := ioutil.ReadAll(res.Body)
		if err != nil {
			c.JSON(http.StatusOK, err.Error())
		}
		c.JSON(http.StatusOK, gin.H{"code": 20000, "data": string(body)})
		return
		//fmt.Print(string(body))
	}
}

func (vms *VirtualMachineServices) MinioLogin(c *gin.Context) {
	apiRequest := MinioLoginRequest{}
	c.ShouldBind(&apiRequest)
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr}

	//accesskey := apiRequest.AccessKey
	//secretkey := apiRequest.SecretKey

	resp, err := client.Post("http://127.0.0.1:9197/api/v1/login",
		"application/json",
		strings.NewReader("{\n  \"accessKey\": \"minioadmin\",\n  \"secretKey\": \"minioadmin\"\n}"))
	if err != nil {
		fmt.Println(err)
	}

	defer resp.Body.Close()
	cookie := resp.Header.Get("Set-Cookie") // + "; domain=192.168.0.101:30852"
	//cookie = strings.Replace(cookie, "Path=/; ", "", 1)
	//cookie = strings.Replace(cookie, "SameSite=Lax;", "SameSite=None;", 1)
	c.Header("Set-Cookie", cookie)

	//c.Status(302)
	//c.Redirect(302, "http://192.168.0.101:30852")
	fmt.Println(cookie)
	return

	//fmt.Print(string(body))
}
